#include "libavutil/avutil.h"
#include "libavformat/avformat.h"

//ffmpeg7
//提取视频aac 对应课程p10
//执行 ./metedata /path/xxx.mp4 out.aac

//备查sampling_frequency_index
const int sampleFrequencyTable[] = {
        96000,  // 0x0
        88200,  // 0x1
        64000,  // 0x2
        48000,  // 0x3
        44100,  // 0x4
        32000,  // 0x5
        24000,  // 0x6
        22050,  // 0x7
        16000,  // 0x8
        12000,  // 0x9
        11025,  // 0xa
        8000,   // 0xb
        7350
};

/*********************************
/*含义：aac-adts header
/*输出：0：编码成功；-1：编码失败
/*输入：p_adts_header:接收编码成功的数据；data_length：data的长度；……
*********************************/
int getADTSHeader(char * const p_adts_header, const int data_length,const int profile, const int samplerate,const int channels)
{

    int sampling_frequency_index = 3; // 默认sampling_frequency_index
    int adtsLen = data_length + 7; //header的长度为7 byte

//通过samplerate匹配sampling_frequency_index
    int frequencies_size = sizeof(sampleFrequencyTable) /
                           sizeof(sampleFrequencyTable[0]);
    int i = 0;
    for(i = 0; i < frequencies_size; i++)
    {
        if(sampleFrequencyTable[i] == samplerate)
        {
            sampling_frequency_index = i;
            break;
        }
    }
    if(i >= frequencies_size)
    {
        printf("unsupport samplerate:%d\n", samplerate);
        return -1;
    }
//开始写header
    p_adts_header[0] = 0xff;         //syncword:0xfff                          高8bits
    p_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bits
    p_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bit
    p_adts_header[1] |= (0 << 1);    //Layer:0                                 2bits
    p_adts_header[1] |= 1;           //protection absent:1                     1bit

    p_adts_header[2] = (profile)<<6;            //profile:profile               2bits
    p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency
    //index:sampling_frequency_index  4bits
    p_adts_header[2] |= (0 << 1);             //private bit:0                   1bit
    p_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels  高1bit

    p_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bits
    p_adts_header[3] |= (0 << 5);               //original：0                1bit
    p_adts_header[3] |= (0 << 4);               //home：0                    1bit
    p_adts_header[3] |= (0 << 3);               //copyright id bit：0        1bit
    p_adts_header[3] |= (0 << 2);               //copyright id start：0      1bit
    p_adts_header[3] |= ((adtsLen & 0x1800) >> 11);   //frame length：value   高2bits

    p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);//frame length:value 中间8bits
    p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);  //frame length:value  低3bits
    p_adts_header[5] |= 0x1f;         //buffer fullness:0x7ff 高5bits
    p_adts_header[6] = 0xfc;      //‭11111100‬       //buffer fullness:0x7ff 低6bits
    // number_of_raw_data_blocks_in_frame：
    //    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。

    return 0;
}

int main(int argc, char **argv) {

    av_log_set_level(AV_LOG_DEBUG);
    if (argc < 3) {
        av_log(NULL, AV_LOG_ERROR, "Usage:%s infile outfile\n", argv[0]);
        return -1;
    }

    const char *infileName = argv[1];
    const char *outfileName = argv[2];

    AVFormatContext *inFormatCtx = NULL;
    int ret = avformat_open_input(&inFormatCtx, infileName, NULL, NULL);
    if (ret != 0) {
        av_log(NULL, AV_LOG_ERROR, "open input file format failed: %s\n", av_err2str(ret));
        return -1;
    }

    ret = avformat_find_stream_info(inFormatCtx, NULL);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "find stream info failed: %s\n", av_err2str(ret));
        return -1;
    }

    int audioIndex = av_find_best_stream(inFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audioIndex < 0) {
        av_log(NULL, AV_LOG_ERROR, "find best stream failed, index is %d\n", audioIndex);
        avformat_close_input(&inFormatCtx);
        return -1;
    }
    av_log(NULL, AV_LOG_INFO, "the audio index is %d\n", audioIndex);

    AVPacket *packet = av_packet_alloc();
    if (!packet) {
        av_log(NULL, AV_LOG_ERROR, "av_packet_alloc failed \n");
        return -1;
    }

    FILE *dest_fp = fopen(outfileName, "wb");
    if (dest_fp == NULL) {
        av_log(NULL, AV_LOG_ERROR, "open %s file failed\n", outfileName);
        avformat_close_input(&inFormatCtx);
        return -1;
    }

    while (av_read_frame(inFormatCtx, packet) == 0) {
        if (packet->stream_index == audioIndex) {
            char adtsHeader[7] = {0};
            getADTSHeader(adtsHeader, packet->size, inFormatCtx->streams[audioIndex]->codecpar->profile,
                          inFormatCtx->streams[audioIndex]->codecpar->sample_rate,
                          inFormatCtx->streams[audioIndex]->codecpar->ch_layout.nb_channels);
            ret = fwrite(adtsHeader, 1, sizeof (adtsHeader), dest_fp);
            if (ret != sizeof adtsHeader) {
                //
            }
            ret = fwrite(packet->data, 1, packet->size, dest_fp);
            if (ret != packet->size) {
                av_log(NULL, AV_LOG_ERROR, "write file failed!\n");
                fclose(dest_fp);
                avformat_close_input(&inFormatCtx);
                return -1;
            }
        }
        av_packet_unref(packet);
    }

    if (inFormatCtx) {
        avformat_close_input(&inFormatCtx);
    }
    if (dest_fp) {
        fclose(dest_fp);
    }

    return 0;
}
